[AWS CDK] パラメーターで渡されたEC2インスタンスのOSイメージ情報で条件分岐してみた
指定されたOSイメージ情報で条件分岐させたい
こんにちは、のんピ(@non____97)です。
皆さんはAWS CDKでパラメーターで渡されたEC2インスタンスのOSイメージ情報で条件分岐させたいと思ったことはありますか? 私はあります。
受け取ったOSイメージ情報に応じて、そのEC2インスタンスに設定するユーザーデータや許可するポート、SSM Patch Managerのベースラインをカスタマイズしたい場面があります。
OSイメージ情報はIMachineImageで渡したいところです。別のプロパティでal2
やwindows
と入力させるのは何となく嫌です。
実際にやってみました。
やってみた
AWS CDKのコード
EC2インスタンスの情報は以下のように指定してあげます。
export const ec2StackParams: ec2StackParams = {
env: {
account: process.env.CDK_DEFAULT_ACCOUNT,
region: process.env.CDK_DEFAULT_REGION,
},
props: {
systemParams: {
systemPrefix: "non-97",
envName: "sandbox",
},
networkParams: {
vpcCidr: "10.10.0.0/20",
subnetConfigurations: [
{
name: "public",
subnetType: cdk.aws_ec2.SubnetType.PUBLIC,
cidrMask: 27,
},
],
maxAzs: 2,
natGateways: 0,
},
ec2Params: {
instances: [
{
instanceName: "web",
machineImage: cdk.aws_ec2.MachineImage.latestAmazonLinux2023({
cachedInContext: true,
}),
instanceType: new cdk.aws_ec2.InstanceType("t3.micro"),
blockDevices: [
{
deviceName: "/dev/xvda",
volume: cdk.aws_ec2.BlockDeviceVolume.ebs(11, {
volumeType: cdk.aws_ec2.EbsDeviceVolumeType.GP3,
encrypted: true,
}),
},
],
subnetSelection: {
subnetType: cdk.aws_ec2.SubnetType.PUBLIC,
},
},
{
instanceName: "ubuntu",
machineImage: cdk.aws_ec2.MachineImage.lookup({
name: "ubuntu/images/hvm-ssd/ubuntu-jammy-22.04-amd64-server-*",
owners: ["099720109477"],
}),
instanceType: new cdk.aws_ec2.InstanceType("t3.micro"),
blockDevices: [
{
deviceName: "/dev/sda1",
volume: cdk.aws_ec2.BlockDeviceVolume.ebs(10, {
volumeType: cdk.aws_ec2.EbsDeviceVolumeType.GP3,
}),
},
],
subnetSelection: {
subnetType: cdk.aws_ec2.SubnetType.PUBLIC,
},
},
{
instanceName: "windows",
machineImage: cdk.aws_ec2.MachineImage.latestWindows(
cdk.aws_ec2.WindowsVersion.WINDOWS_SERVER_2022_JAPANESE_FULL_BASE
),
instanceType: new cdk.aws_ec2.InstanceType("t3.medium"),
blockDevices: [
{
deviceName: "/dev/sda1",
volume: cdk.aws_ec2.BlockDeviceVolume.ebs(30, {
volumeType: cdk.aws_ec2.EbsDeviceVolumeType.GP3,
}),
},
],
subnetSelection: {
subnetType: cdk.aws_ec2.SubnetType.PUBLIC,
},
},
{
instanceName: "al2",
machineImage: cdk.aws_ec2.MachineImage.latestAmazonLinux2({
cachedInContext: false,
}),
instanceType: new cdk.aws_ec2.InstanceType("t3.micro"),
blockDevices: [
{
deviceName: "/dev/xvda",
volume: cdk.aws_ec2.BlockDeviceVolume.ebs(10, {
volumeType: cdk.aws_ec2.EbsDeviceVolumeType.GP3,
encrypted: true,
}),
},
],
subnetSelection: {
subnetType: cdk.aws_ec2.SubnetType.PUBLIC,
},
},
{
instanceName: "rhel",
machineImage: cdk.aws_ec2.MachineImage.lookup({
name: "RHEL-9.3.0_HVM-20240117-x86_64-49-Hourly2-GP3",
owners: ["309956199498"],
}),
instanceType: new cdk.aws_ec2.InstanceType("t3.micro"),
blockDevices: [
{
deviceName: "/dev/sda1",
volume: cdk.aws_ec2.BlockDeviceVolume.ebs(12),
},
],
subnetSelection: {
subnetType: cdk.aws_ec2.SubnetType.PUBLIC,
},
},
],
},
},
};
machineImage
とは別にal2
など指定したくありません。machineImage
で指定できるIMachineImageの実装には以下のようなものがあります。
そのためinstanceof
で判定をすれば、Amazon Linux 2、Amazon Linux 2023、Windowsの判定は簡単に行えそうです。
では、RHELやUbuntuなどLookupMachineImage
の判定はどうすれば良いでしょうか。
考えられる案は以下の3パターンです。
LookupMachineImage
のGetterやpublicなメンバー変数があれば、そこから指定されたname
やowners
を元に判定する- AWS CDKでsynthした際にAMIのImage IDが取得できるので、AWS SDKでDescribeImagesを叩いて、取得した情報を元に判定する
- AWS CDKでsynthした際にAMIのImage IDが取得できるので、AWS CDKのcontextキーを逆引きして、取得したキー情報を元に判定する
1つ目のパターンはLookupMachineImage
の関数はgetImage()
しか存在せず、メンバー変数に直接アクセスすることもできないため、対応できません。要するに以下のようなことはできません。
const lookupMachineImage = new ec2.LookupMachineImage({
name: 'name',
owners: ['owners'],
});
const imageName = lookupMachineImage.name
const imageOwners = lookupMachineImage.getOwners()
2つ目のパターンについてはMFAを強制していない環境では対応が可能と考えます。以下のようにすれ取得したImage IDから、より詳細な情報を取得して判定ができそうです。
const image = await this.getImageInfo(
machineImage.getImage(this).imageId
);
.
.
(中略)
.
.
async getImageInfo(imageId: string): Promise<Image | undefined> {
const ec2Client = new EC2Client({})
const describeImagesCommand = new DescribeImagesCommand({
ImageIds: [imageId],
});
try {
const response = await ec2Client.send(describeImagesCommand);
return response.Images?.[0];
} catch (error) {
console.error(error);
throw new Error("Error describing images");
}
}
ただし、MFAを強制している環境では上手くいきません。AWS CDKを実行するときにMFAを入力することになるのですが、よしなにAWS SDKに渡してくれはしません。AWS SDKを実行する際に再度MFAの入力を行う必要があります。しかし、AWS CDKでsynthをしている際は、どうも標準入力の受付を待機してくれないようです。MFAの入力さえ受け付けてくれれば@aws-sdk/credential-providers の fromTemporaryCredentials()で何とかできそうではあります。
他の回避策としてはAWS CDKで使用している認証情報をAWS SDKのクライアントに渡すことが考えられます。残念ながらこちらも2.133.0時点では都合の良い連携方法はありませんでした。
となると、残りは3つ目のパターンです。
AWS CDKではcontextの値から直接キーを取得する方法はありません。また、contextの一覧を取得するというのもsynth中に行うことはできなさそうでした。ということでcdk.context.json
を読み込んで、そこから判定します。
取得するcontextのキーはami:account=123456789012:filters.image-type.0=machine:filters.name.0=ubuntu/images/hvm-ssd/ubuntu-jammy-22.04-amd64-server-*:filters.state.0=available:owners.0=099720109477:region=us-east-1": "ami-0e21465cede02fd1e
というようなフォーマットです。
このままだと扱いにくいので以下のようにパースをしてあげてから判定をします。判定されたOSイメージ情報に応じて実行するユーザーデータを変更してみます。
{
account: '自身のAWSアカウントID',
filters: {
'image-type': [ 'machine' ],
name: [ 'LookupMachineImage で指定したAMIの名前' ],
state: [ 'available' ]
},
owners: [ 'LookupMachineImage で指定したAMIの所有者のAWSアカウントID' ],
region: 'us-east-1'
EC2インスタンスを作成するコンストラクトのコードは以下の通りです。
import * as cdk from "aws-cdk-lib";
import { Construct } from "constructs";
import { SystemParams, Ec2Params } from "../../parameter/index";
import { NetworkConstruct } from "./network-construct";
import * as fs from "fs";
import * as path from "path";
export interface Ec2ConstructProps extends SystemParams, Ec2Params {
networkConstruct: NetworkConstruct;
}
interface ImageContext {
account: string;
filters: {
"image-type"?: string[];
name?: string[];
state?: string[];
[key: string]: string[] | undefined;
};
owners: string[];
region: string;
}
interface SupportOs {
osName: string;
machineImage: {
namePattern: string;
ownerId: string;
};
}
const supportOses: SupportOs[] = [
{
osName: "al2",
machineImage: {
namePattern: "amzn2-ami.*",
ownerId: "137112412989",
},
},
{
osName: "al2023",
machineImage: {
namePattern: "al2023-ami.*",
ownerId: "137112412989",
},
},
{
osName: "windows",
machineImage: {
namePattern: "Windows_Server-.*",
ownerId: "801119661308",
},
},
{
osName: "rhel",
machineImage: {
namePattern: "RHEL-.*",
ownerId: "309956199498",
},
},
{
osName: "ubuntu",
machineImage: {
namePattern: ".*ubuntu-.*",
ownerId: "099720109477",
},
},
];
export class Ec2Construct extends Construct {
readonly instances: {
instanceName: string;
instance: cdk.aws_ec2.Instance;
}[];
constructor(scope: Construct, id: string, props: Ec2ConstructProps) {
super(scope, id);
// サポートOS判定
const osNames = [
...new Set(
props.instances
.map((instanceProps) => {
return this.isSupportedOs(instanceProps.machineImage);
})
.filter((osName): osName is string => !!osName)
),
];
// サポート対象のOSだった場合はユーザーデータを組み立て
const userDataOsNameMappings = osNames.map((osName) => {
const userData =
osName === "windows"
? cdk.aws_ec2.UserData.forWindows()
: cdk.aws_ec2.UserData.forLinux();
const userDataScript =
osName === "windows"
? fs.readFileSync(
path.join(__dirname, `../ec2-settings/user-data/windows.ps1`),
"utf8"
)
: fs.readFileSync(
path.join(__dirname, `../ec2-settings/user-data/${osName}.sh`),
"utf8"
);
userData.addCommands(
userDataScript
.replace(/__SYSTEM_PREFIX__/g, props.systemPrefix)
.replace(/__ENV_NAME__/g, props.envName)
);
return { osName, userData };
});
// IAM Role
const role = new cdk.aws_iam.Role(this, "Role", {
assumedBy: new cdk.aws_iam.ServicePrincipal("ec2.amazonaws.com"),
managedPolicies: [
cdk.aws_iam.ManagedPolicy.fromAwsManagedPolicyName(
"AmazonSSMManagedInstanceCore"
),
cdk.aws_iam.ManagedPolicy.fromAwsManagedPolicyName(
"CloudWatchAgentServerPolicy"
),
],
roleName: `${props.systemPrefix}-${props.envName}-role-ec2`,
});
const cfnInstanceProfile = new cdk.aws_iam.CfnInstanceProfile(
this,
"CfnInstanceProfile",
{
instanceProfileName: `${props.systemPrefix}-${props.envName}-instance-profile-ec2`,
roles: [role.roleName],
}
);
// EC2 Instances
this.instances = props.instances.map((instanceProps) => {
const instanceSuffix = instanceProps.instanceName
? `-${instanceProps.instanceName}`
: "";
const instanceName = `${props.systemPrefix}-${props.envName}-ec2${instanceSuffix}`;
// Security Group
const securityGroupName = `${props.systemPrefix}-${props.envName}-sg-ec2${instanceSuffix}`;
const securityGroup = new cdk.aws_ec2.SecurityGroup(
this,
`SecurityGroup${instanceSuffix}`,
{
vpc: props.networkConstruct.vpc,
securityGroupName,
description: `Security Group for ${props.systemPrefix} ${props.envName} EC2 Instance ${instanceSuffix}`,
}
);
cdk.Tags.of(securityGroup).add("Name", securityGroupName);
// Instance
const instance = new cdk.aws_ec2.Instance(
this,
`Instance${instanceSuffix}`,
{
machineImage: instanceProps.machineImage,
instanceType: instanceProps.instanceType,
vpc: props.networkConstruct.vpc,
vpcSubnets: props.networkConstruct.vpc.selectSubnets(
instanceProps.subnetSelection
),
blockDevices: instanceProps.blockDevices,
propagateTagsToVolumeOnCreation: true,
role,
requireImdsv2: true,
userData: userDataOsNameMappings.find((userDataOsNameMapping) => {
return (
userDataOsNameMapping.osName ===
this.isSupportedOs(instanceProps.machineImage)
);
})?.userData,
securityGroup,
instanceName: `${props.systemPrefix}-${props.envName}-ec2${instanceSuffix}`,
}
);
// Instance profile
instance.node.tryRemoveChild("InstanceProfile");
const cfnInstance = instance.node.tryFindChild(
"Resource"
) as cdk.aws_ec2.CfnInstance;
cfnInstance.addDependency(cfnInstanceProfile);
cfnInstance.addPropertyOverride(
"IamInstanceProfile",
cfnInstanceProfile.ref
);
return { instanceName, instance };
});
}
// サポート対象のOSかどうか判定
isSupportedOs = (machineImage: cdk.aws_ec2.IMachineImage) => {
// AL 2023
if (machineImage instanceof cdk.aws_ec2.AmazonLinux2023ImageSsmParameter) {
return "al2023";
}
// AL 2
else if (
machineImage instanceof cdk.aws_ec2.AmazonLinux2ImageSsmParameter
) {
return "al2";
}
// Windows
else if (machineImage instanceof cdk.aws_ec2.WindowsImage) {
return "windows";
}
// Lookup
else if (machineImage instanceof cdk.aws_ec2.LookupMachineImage) {
// Image IDの取得
const imageId = machineImage.getImage(this).imageId;
// 取得したImage IDが ami-1234 の場合はcontext.cdk.jsonに記録されていない場合
// この場合は再度AMIの検索がかかる
if (imageId === "ami-1234") {
console.log("AMI not found in context.cdk.json");
return undefined;
}
// ami-1234 以外でImage IDが指定されていた場合
else if (imageId) {
// cdk.context.jsonから対象のImage IDのキーを取得する
const contextJson = JSON.parse(
fs.readFileSync(
path.join(__dirname, "../../cdk.context.json"),
"utf8"
)
);
const imageContextKey = Object.keys(contextJson).find((key: string) => {
return contextJson[key] === imageId;
});
if (!imageContextKey) {
return;
}
// cdk.context.jsonのキーをパースする
const imageContext = this.parseImageContext(imageContextKey);
// パースしたcdk.context.jsonのキーとサポート対象のOSのオブジェクトの配列を突き合わせて、サポート対象かどうか判定する
const supportOs = supportOses.find((supportOs) => {
const imageNamePattern = new RegExp(
supportOs.machineImage.namePattern
);
return (
imageContext.filters.name &&
imageContext.filters.name.filter((name) =>
imageNamePattern.test(name)
).length > 0 &&
imageContext.owners.filter(
(owner) => owner === supportOs.machineImage.ownerId
).length > 0
);
});
if (supportOs) {
return supportOs.osName;
} else {
throw new Error("Unsupported machine image type");
}
} else {
throw new Error("AMI not found");
}
}
// 不明な型の場合はサポート対象外と判定する
else {
throw new Error("Unsupported machine image type");
}
};
// cdk.context.jsonのImage keyのParse
parseImageContext(imageContextKey: string): ImageContext {
// imageContextKey は以下のようなフォーマット
// "ami:account=123456789012:filters.image-type.0=machine:filters.name.0=ubuntu/images/hvm-ssd/ubuntu-jammy-22.04-amd64-server-*:filters.state.0=available:owners.0=099720109477:region=us-east-1": "ami-0e21465cede02fd1e"
const imageContext: ImageContext = {
account: "",
filters: {},
owners: [],
region: "",
};
const keyValueStrings = imageContextKey.split(":");
const prefix = keyValueStrings.shift();
if (prefix !== "ami") {
throw new Error("Invalid image context key format");
}
// "=" をデリミタとしてパース
for (const keyValueString of keyValueStrings) {
const [key, value] = keyValueString.split("=");
const parsedKey = key.replace(/\./g, "_");
if (parsedKey === "account") {
imageContext.account = value;
} else if (parsedKey.startsWith("filters_")) {
const [_, filterName] = parsedKey.split("_");
if (!imageContext.filters[filterName]) {
imageContext.filters[filterName] = [];
}
imageContext.filters[filterName]!.push(value);
} else if (parsedKey.startsWith("owners_")) {
imageContext.owners = value.split(",");
} else if (parsedKey === "region") {
imageContext.region = value;
}
}
return imageContext;
}
}
その他のコードは以下GitHubリポジトリをご覧ください。
デプロイしてみる
それでは実際にデプロイしてみましょう。
AWS CDKのcontextの情報は以下のとおりです。AMIの情報はキャッシュされていません。
$ npx cdk context
Context found in cdk.json:
┌────┬───────────────────────────────────────────────────────────────────────────────────────┬────────────────────────────────────────────────────────────────────────────────────────┐
│ # │ Key │ Value │
├────┼───────────────────────────────────────────────────────────────────────────────────────┼────────────────────────────────────────────────────────────────────────────────────────┤
│ 1 │ @aws-cdk-containers/ecs-service-extensions:enableDefaultLogDriver │ true │
├────┼───────────────────────────────────────────────────────────────────────────────────────┼────────────────────────────────────────────────────────────────────────────────────────┤
│ 2 │ @aws-cdk/aws-apigateway:authorizerChangeDeploymentLogicalId │ true │
├────┼───────────────────────────────────────────────────────────────────────────────────────┼────────────────────────────────────────────────────────────────────────────────────────┤
.
.
(中略)
.
.
├────┼───────────────────────────────────────────────────────────────────────────────────────┼────────────────────────────────────────────────────────────────────────────────────────┤
│ 44 │ availability-zones:account=<AWSアカウントID>:region=us-east-1 │ [ "us-east-1a", "us-east-1b", "us-east-1c", "us-east-1d", "us-east-1e", "us-east-1f" ] │
└────┴───────────────────────────────────────────────────────────────────────────────────────┴────────────────────────────────────────────────────────────────────────────────────────┘
Run cdk context --reset KEY_OR_NUMBER to remove a context key. It will be refreshed on the next CDK synthesis run.
デプロイします。
$ npx cdk deploy
AMI not found in context.cdk.json
AMI not found in context.cdk.json
AMI not found in context.cdk.json
AMI not found in context.cdk.json
AMI not found in context.cdk.json
AMI not found in context.cdk.json
AMI not found in context.cdk.json
AMI not found in context.cdk.json
Searching for AMI in <AWSアカウントID>:us-east-1
Searching for AMI in <AWSアカウントID>:us-east-1
✨ Synthesis time: 20.44s
cicd-non-97-sandbox: start: Building 0d69c41138981a5aef0cbdb83a1414dedb3d704752c4162bbc7164b12b2c221e:<AWSアカウントID>-us-east-1
cicd-non-97-sandbox: success: Built 0d69c41138981a5aef0cbdb83a1414dedb3d704752c4162bbc7164b12b2c221e:<AWSアカウントID>-us-east-1
cicd-non-97-sandbox: start: Publishing 0d69c41138981a5aef0cbdb83a1414dedb3d704752c4162bbc7164b12b2c221e:<AWSアカウントID>-us-east-1
cicd-non-97-sandbox: success: Published 0d69c41138981a5aef0cbdb83a1414dedb3d704752c4162bbc7164b12b2c221e:<AWSアカウントID>-us-east-1
This deployment will make potentially sensitive changes according to your current security approval level (--require-approval broadening).
Please confirm you intend to make the following modifications:
IAM Statement Changes
┌───┬──────────────────────────────────────────────────────────────────┬────────┬──────────────────────────────────────────────────────────────────┬───────────────────────────────────────────────────────────────────┬───────────┐
│ │ Resource │ Effect │ Action │ Principal │ Condition │
├───┼──────────────────────────────────────────────────────────────────┼────────┼──────────────────────────────────────────────────────────────────┼───────────────────────────────────────────────────────────────────┼───────────┤
│ + │ ${Custom::VpcRestrictDefaultSGCustomResourceProvider/Role.Arn} │ Allow │ sts:AssumeRole │ Service:lambda.amazonaws.com │ │
├───┼──────────────────────────────────────────────────────────────────┼────────┼──────────────────────────────────────────────────────────────────┼───────────────────────────────────────────────────────────────────┼───────────┤
│ + │ ${Ec2Construct/Role.Arn} │ Allow │ sts:AssumeRole │ Service:ec2.amazonaws.com │ │
├───┼──────────────────────────────────────────────────────────────────┼────────┼──────────────────────────────────────────────────────────────────┼───────────────────────────────────────────────────────────────────┼───────────┤
│ + │ arn:aws:ec2:us-east-1:<AWSアカウントID>:security-group/${NetworkConst │ Allow │ ec2:AuthorizeSecurityGroupEgress │ AWS:${Custom::VpcRestrictDefaultSGCustomResourceProvider/Role} │ │
│ │ ruct/Default.DefaultSecurityGroup} │ │ ec2:AuthorizeSecurityGroupIngress │ │ │
│ │ │ │ ec2:RevokeSecurityGroupEgress │ │ │
│ │ │ │ ec2:RevokeSecurityGroupIngress │ │ │
└───┴──────────────────────────────────────────────────────────────────┴────────┴──────────────────────────────────────────────────────────────────┴───────────────────────────────────────────────────────────────────┴───────────┘
IAM Policy Changes
┌───┬────────────────────────────────────────────────────────────┬──────────────────────────────────────────────────────────────────────────────────────────────┐
│ │ Resource │ Managed Policy ARN │
├───┼────────────────────────────────────────────────────────────┼──────────────────────────────────────────────────────────────────────────────────────────────┤
│ + │ ${Custom::VpcRestrictDefaultSGCustomResourceProvider/Role} │ {"Fn::Sub":"arn:${AWS::Partition}:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"} │
├───┼────────────────────────────────────────────────────────────┼──────────────────────────────────────────────────────────────────────────────────────────────┤
│ + │ ${Ec2Construct/Role} │ arn:${AWS::Partition}:iam::aws:policy/AmazonSSMManagedInstanceCore │
│ + │ ${Ec2Construct/Role} │ arn:${AWS::Partition}:iam::aws:policy/CloudWatchAgentServerPolicy │
└───┴────────────────────────────────────────────────────────────┴──────────────────────────────────────────────────────────────────────────────────────────────┘
Security Group Changes
┌───┬───────────────────────────────────────────────┬─────┬────────────┬─────────────────┐
│ │ Group │ Dir │ Protocol │ Peer │
├───┼───────────────────────────────────────────────┼─────┼────────────┼─────────────────┤
│ + │ ${Ec2Construct/SecurityGroup-al2.GroupId} │ Out │ Everything │ Everyone (IPv4) │
├───┼───────────────────────────────────────────────┼─────┼────────────┼─────────────────┤
│ + │ ${Ec2Construct/SecurityGroup-rhel.GroupId} │ Out │ Everything │ Everyone (IPv4) │
├───┼───────────────────────────────────────────────┼─────┼────────────┼─────────────────┤
│ + │ ${Ec2Construct/SecurityGroup-ubuntu.GroupId} │ Out │ Everything │ Everyone (IPv4) │
├───┼───────────────────────────────────────────────┼─────┼────────────┼─────────────────┤
│ + │ ${Ec2Construct/SecurityGroup-web.GroupId} │ Out │ Everything │ Everyone (IPv4) │
├───┼───────────────────────────────────────────────┼─────┼────────────┼─────────────────┤
│ + │ ${Ec2Construct/SecurityGroup-windows.GroupId} │ Out │ Everything │ Everyone (IPv4) │
└───┴───────────────────────────────────────────────┴─────┴────────────┴─────────────────┘
(NOTE: There may be security-related changes not in this list. See https://github.com/aws/aws-cdk/issues/1299)
Do you wish to deploy these changes (y/n)? y
cicd-non-97-sandbox: deploying... [1/1]
cicd-non-97-sandbox: creating CloudFormation changeset...
✅ cicd-non-97-sandbox
✨ Deployment time: 186.5s
Stack ARN:
arn:aws:cloudformation:us-east-1:<AWSアカウントID>:stack/cicd-non-97-sandbox/e5622290-e822-11ee-9d49-0affda88fcdf
✨ Total time: 206.94s
$ npx cdk context
Context found in cdk.json:
┌────┬──────────────────────────────────────────────────────────────────────────────────────────────────────────────┬──────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
│ # │ Key │ Value │
├────┼──────────────────────────────────────────────────────────────────────────────────────────────────────────────┼──────────────────────────────────────────────────────────────────────────────────────────────────────────────┤
│ 1 │ @aws-cdk-containers/ecs-service-extensions:enableDefaultLogDriver │ true │
├────┼──────────────────────────────────────────────────────────────────────────────────────────────────────────────┼──────────────────────────────────────────────────────────────────────────────────────────────────────────────┤
│ 2 │ @aws-cdk/aws-apigateway:authorizerChangeDeploymentLogicalId │ true │
├────┼──────────────────────────────────────────────────────────────────────────────────────────────────────────────┼──────────────────────────────────────────────────────────────────────────────────────────────────────────────┤
.
.
(中略)
.
.
├────┼──────────────────────────────────────────────────────────────────────────────────────────────────────────────┼──────────────────────────────────────────────────────────────────────────────────────────────────────────────┤
│ 43 │ @aws-cdk/customresources:installLatestAwsSdkDefault │ false │
├────┼──────────────────────────────────────────────────────────────────────────────────────────────────────────────┼──────────────────────────────────────────────────────────────────────────────────────────────────────────────┤
│ 44 │ ami:account=<AWSアカウントID>:filters.image-type.0=machine:filters.name.0=RHEL-9.3.0_HVM-20240117-x86_64-49-Hourl │ "ami-0fe630eb857a6ec83" │
│ │ y2-GP3:filters.state.0=available:owners.0=309956199498:region=us-east-1 │ │
├────┼──────────────────────────────────────────────────────────────────────────────────────────────────────────────┼──────────────────────────────────────────────────────────────────────────────────────────────────────────────┤
│ 45 │ ami:account=<AWSアカウントID>:filters.image-type.0=machine:filters.name.0=ubuntu/images/hvm-ssd/ubuntu-jammy-22.0 │ "ami-0e21465cede02fd1e" │
│ │ 4-amd64-server-*:filters.state.0=available:owners.0=099720109477:region=us-east-1 │ │
├────┼──────────────────────────────────────────────────────────────────────────────────────────────────────────────┼──────────────────────────────────────────────────────────────────────────────────────────────────────────────┤
│ 46 │ availability-zones:account=<AWSアカウントID>:region=us-east-1 │ [ "us-east-1a", "us-east-1b", "us-east-1c", "us-east-1d", "us-east-1e", "us-east-1f" ] │
├────┼──────────────────────────────────────────────────────────────────────────────────────────────────────────────┼──────────────────────────────────────────────────────────────────────────────────────────────────────────────┤
│ 47 │ ssm:account=<AWSアカウントID>:parameterName=/aws/service/ami-amazon-linux-latest/al2023-ami-kernel-6.1-x86_64:reg │ "ami-0c101f26f147fa7fd" │
│ │ ion=us-east-1 │ │
└────┴──────────────────────────────────────────────────────────────────────────────────────────────────────────────┴──────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
Run cdk context --reset KEY_OR_NUMBER to remove a context key. It will be refreshed on the next CDK synthesis run.
問題なくデプロイできました。contextにもcachedInContext
をtrue
にしたものやMachineImage.lookup
で指定したものはキャッシュされていますね。
作成されたEC2インスタンスのユーザーデータは以下のとおりです。
$ cat /etc/os-release
NAME="Amazon Linux"
VERSION="2023"
ID="amzn"
ID_LIKE="fedora"
VERSION_ID="2023"
PLATFORM_ID="platform:al2023"
PRETTY_NAME="Amazon Linux 2023.4.20240319"
ANSI_COLOR="0;33"
CPE_NAME="cpe:2.3:o:amazon:amazon_linux:2023"
HOME_URL="https://aws.amazon.com/linux/amazon-linux-2023/"
DOCUMENTATION_URL="https://docs.aws.amazon.com/linux/"
SUPPORT_URL="https://aws.amazon.com/premiumsupport/"
BUG_REPORT_URL="https://github.com/amazonlinux/amazon-linux-2023"
VENDOR_NAME="AWS"
VENDOR_URL="https://aws.amazon.com/"
SUPPORT_END="2028-03-15"
$ cat /var/log/user-data.log
+ declare -r SYSTEM_PREFIX=non-97
+ declare -r ENV_NAME=sandbox
+ echo 'non-97 sandbox AL2023 Instance'
non-97 sandbox AL2023 Instance
$ cat /etc/os-release
PRETTY_NAME="Ubuntu 22.04.4 LTS"
NAME="Ubuntu"
VERSION_ID="22.04"
VERSION="22.04.4 LTS (Jammy Jellyfish)"
VERSION_CODENAME=jammy
ID=ubuntu
ID_LIKE=debian
HOME_URL="https://www.ubuntu.com/"
SUPPORT_URL="https://help.ubuntu.com/"
BUG_REPORT_URL="https://bugs.launchpad.net/ubuntu/"
PRIVACY_POLICY_URL="https://www.ubuntu.com/legal/terms-and-policies/privacy-policy"
UBUNTU_CODENAME=jammy
$ cat /var/log/user-data.log
+ declare -r SYSTEM_PREFIX=non-97
+ declare -r ENV_NAME=sandbox
+ echo 'non-97 sandbox Ubuntu Instance'
non-97 sandbox Ubuntu Instance
$ cat /etc/os-release
NAME="Amazon Linux"
VERSION="2"
ID="amzn"
ID_LIKE="centos rhel fedora"
VERSION_ID="2"
PRETTY_NAME="Amazon Linux 2"
ANSI_COLOR="0;33"
CPE_NAME="cpe:2.3:o:amazon:amazon_linux:2"
HOME_URL="https://amazonlinux.com/"
SUPPORT_END="2025-06-30"
$ cat /var/log/user-data.log
+ declare -r SYSTEM_PREFIX=non-97
+ declare -r ENV_NAME=sandbox
+ echo 'non-97 sandbox AL2 Instance'
non-97 sandbox AL2 Instance
> Get-WmiObject Win32_OperatingSystem
SystemDirectory : C:\Windows\system32
Organization : Amazon.com
BuildNumber : 20348
RegisteredUser : EC2
SerialNumber : 00454-60000-00001-AA671
Version : 10.0.20348
> cat C:\Windows\system32\config\systemprofile\AppData\Local\Temp\EC2Launch2080308524\output.tmp
non-97 sandbox Windows Instance
$ cat /etc/os-release
NAME="Red Hat Enterprise Linux"
VERSION="9.3 (Plow)"
ID="rhel"
ID_LIKE="fedora"
VERSION_ID="9.3"
PLATFORM_ID="platform:el9"
PRETTY_NAME="Red Hat Enterprise Linux 9.3 (Plow)"
ANSI_COLOR="0;31"
LOGO="fedora-logo-icon"
CPE_NAME="cpe:/o:redhat:enterprise_linux:9::baseos"
HOME_URL="https://www.redhat.com/"
DOCUMENTATION_URL="https://access.redhat.com/documentation/en-us/red_hat_enterprise_linux/9"
BUG_REPORT_URL="https://bugzilla.redhat.com/"
REDHAT_BUGZILLA_PRODUCT="Red Hat Enterprise Linux 9"
REDHAT_BUGZILLA_PRODUCT_VERSION=9.3
REDHAT_SUPPORT_PRODUCT="Red Hat Enterprise Linux"
REDHAT_SUPPORT_PRODUCT_VERSION="9.3"
$ cat /var/log/user-data.log
+ declare -r SYSTEM_PREFIX=non-97
+ declare -r ENV_NAME=sandbox
++ curl -s -X PUT -H 'X-aws-ec2-metadata-token-ttl-seconds: 21600' http://169.254.169.254/latest/api/token
+ token=AQAEAINkCALNEEsNiOZPMH-KMT-vKCZd0jh29RPQkncyq_BzCaMlew==
++ sed -e 's/.$//'
++ curl -s -H 'X-aws-ec2-metadata-token: AQAEAINkCALNEEsNiOZPMH-KMT-vKCZd0jh29RPQkncyq_BzCaMlew==' http://169.254.169.254/latest/meta-data/placement/availability-zone
+ region_name=us-east-1
+ dnf install -y https://s3.us-east-1.amazonaws.com/amazon-ssm-us-east-1/latest/linux_amd64/amazon-ssm-agent.rpm
Updating Subscription Management repositories.
Unable to read consumer identity
This system is not registered with an entitlement server. You can use subscription-manager to register.
Red Hat Enterprise Linux 9 for x86_64 - AppStre 35 MB/s | 30 MB 00:00
Red Hat Enterprise Linux 9 for x86_64 - BaseOS 1.2 MB/s | 19 MB 00:16
Red Hat Enterprise Linux 9 Client Configuration 422 B/s | 2.2 kB 00:05
amazon-ssm-agent.rpm 79 MB/s | 26 MB 00:00
Dependencies resolved.
================================================================================
Package Architecture Version Repository Size
================================================================================
Installing:
amazon-ssm-agent x86_64 3.3.131.0-1 @commandline 26 M
Transaction Summary
================================================================================
Install 1 Package
Total size: 26 M
Installed size: 108 M
Downloading Packages:
Running transaction check
Transaction check succeeded.
Running transaction test
Transaction test succeeded.
Running transaction
Running scriptlet: amazon-ssm-agent-3.3.131.0-1.x86_64 1/1
Preparing : 1/1
Running scriptlet: amazon-ssm-agent-3.3.131.0-1.x86_64 1/1
Installing : amazon-ssm-agent-3.3.131.0-1.x86_64 1/1
Running scriptlet: amazon-ssm-agent-3.3.131.0-1.x86_64 1/1
Created symlink /etc/systemd/system/multi-user.target.wants/amazon-ssm-agent.service → /etc/systemd/system/amazon-ssm-agent.service.
Verifying : amazon-ssm-agent-3.3.131.0-1.x86_64 1/1
Installed products updated.
Installed:
amazon-ssm-agent-3.3.131.0-1.x86_64
Complete!
+ systemctl enable amazon-ssm-agent --now
+ echo 'non-97 sandbox RHEL Instance'
non-97 sandbox RHEL Instance
それぞれのOSイメージごとに用意したユーザーデータのスクリプトを使っていますね。
真面目にやろうとすると意外と大変
AWS CDKでパラメーターとして渡されたEC2インスタンスのOSイメージ情報で条件分岐してみました。
真面目にやろうとすると意外と大変でした。
ちなみにLookupMachineImage
で指定するfilters
はDescribeImages APIのFilter
です。CPUアーキテクチャやタグなどを使って判定することもできそうですね。
この記事が誰かの助けになれば幸いです。
以上、AWS事業本部 コンサルティング部の のんピ(@non____97)でした!